#include "StdAfx.h"
#include "Core.h"
#include "CharacterOperations.h"
#include <iostream>

using std::string;				using std::cout;				
using std::vector;				using std::endl;
using std::runtime_error;		using std::cerr;

/* Constructor / Descructor */
Core::Core(string filePath)
{
	file = new CodeFile(filePath);
}

Core::~Core()
{
	delete file;
}	

/* Main Compilation Method */
string Core::Compile()
{
	// Prepare by getting the first character and skipping whitespace
	file->GetNextChar();
	file->SkipWhite();
	Assignment();

	string compiledReturn;

	for(auto iter = compiledObject.begin(); iter != compiledObject.end(); iter++)
	{
		compiledReturn += "\n" + *iter;
	}

	return compiledReturn;
}

// <expression> == <term> [<addop> <term>]*
void Core::Expression()
{
	Term();
	Additive();
}

// [<addop> <term>]*
void Core::Additive()
{
	while(IsAddOp(file->Lookahead))
	{
		compiledObject.push_back("PUSH eax;");

		switch(file->Lookahead)
		{
		case '+':
			Add();
			break;
		case '-':
			Subtract();
			break;
		}
	}
}

// <term> == <factor> [<multop> <factor>]*
void Core::Term()
{	
	Factor();	
		
	while(IsMulOp(file->Lookahead))
	{
		compiledObject.push_back("PUSH eax;");

		switch(file->Lookahead)
		{
		case '*':
			Multiply();
			break;
		case '/':
			Divide();
			break;
		}
	}
}

// <factor> == <number> | (<expression>) | <variable>
void Core::Factor()
{
    if(file->Lookahead == '(')
	{
		file->Match('(');
		Expression();
		file->Match(')');
	}
	else if(IsAddOp(file->Lookahead))
	{
		compiledObject.push_back("XOR eax, eax;");
		Additive();
	}
	else if(IsLetter(file->Lookahead))
	{
		Ident();
	}
	else
	{
		compiledObject.push_back("MOV eax, " + file->GetNum() + ";");
	}
}

// <ident> = <expression> (NOTE: assignment not equality)
void Core::Ident()
{
	string identifier = file->GetName();

	if(file->Lookahead == '(')
	{
		file->Match('(');
		file->Match(')');
		compiledObject.push_back("CALL " + identifier + ";");
	}
	else
	{
		compiledObject.push_back("MOV eax, " + identifier + ";");
	}
}

void Core::Assignment()
{
	string identifier = file->GetName();
	file->Match('=');
	Expression();
	compiledObject.push_back("MOV " + identifier + ", eax;");
}

/* Arithmetic Operations */
void Core::Add()
{
	file->Match('+');
	Term();
	compiledObject.push_back("POP ebx;");
	compiledObject.push_back("ADD eax, ebx;\n");
}

void Core::Subtract()
{
	file->Match('-');
	Term();
	compiledObject.push_back("POP ebx;");
	compiledObject.push_back("SUB eax, ebx;\n");
	compiledObject.push_back("NEG eax;");
}

void Core::Multiply()
{
	file->Match('*');
	Factor();
	compiledObject.push_back("POP ebx;");
	compiledObject.push_back("MUL ebx;\n");
}

void Core::Divide()
{
	file->Match('/');
	Factor();
	compiledObject.push_back("XOR edx, edx;");
	compiledObject.push_back("MOV ebx, eax;");
	compiledObject.push_back("POP eax;");
	compiledObject.push_back("IDIV ebx;\n");
}
